// epoll
/*
    epoll默认模式 LT
    LT（水平触发Level Triggered）：事件到来，但上层不处理，一直有效
    
    ET（边缘触发Edge Triggered）：数据从无到有，从有到多，变化的时候，才会通知我们
    ET 的通知相比LT较高，IO效率也比LT高
    ET 的特点导致每次通知都要求我们必须把本轮数据全部取走
        循环读取，读取出错，    
        ET模式下所有的fd必须是非阻塞的
*/
/*接口
    epoll_creat 打开了一个模型
    epoll_wait          返回就绪的fd个数
    epoll_ctl   管理epoll ADD/MOD/DEL/
*/

// 缺点
/*

*/
#pragma once

#include <iostream>
#include <unistd.h>
#include<memory>
#include <sys/epoll.h>
#include "sockfd.hpp"
#include "Epoller.hpp"
#include "nocopy.hpp"
#include "Log.hpp"
using namespace std;

uint32_t EVENT_IN = (EPOLLIN);   // 读
uint32_t EVENT_OUT = (EPOLLOUT); // 写
static const uint16_t defaultport = 8080;

class EpollServer : public nocopy
{
    static const int num = 64;

private:
    uint16_t _port;
    std::shared_ptr<Sock> _listen_ptr;
    std::shared_ptr<Epoller> _epoller_ptr;

public:
    EpollServer(uint16_t port = defaultport) 
    : _port(port),
    _listen_ptr(new Sock()),
    _epoller_ptr(new Epoller())
    {
    }
    void Init()
    {
        _listen_ptr->Socket();
        _listen_ptr->Bind(_port);
        _listen_ptr->Listen();
        lg(Info, "creat listen sock success: %d\n", _listen_ptr->Fd());
    }
    void Accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int sock = _listen_ptr->Accept(&clientip, &clientport);
        if (sock > 0)
        {
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, EVENT_IN);
            lg(Info,"get a new link client info:%s:%d",clientip.c_str(),clientport);
        }
        else
            return;
    }
    void Recver(int fd)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "get a message: " << buffer << endl;
            std::string echo_str = "server $ ";
            echo_str += buffer;
            write(fd,echo_str.c_str(),echo_str.size());
        }
        else if (n == 0)
        {
            lg(Info, "client quit,close fd:%d", fd);
            _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
            close(fd);
        }
        else
        {
            lg(Warning, "read error fd:%d", fd);
            close(fd);
        }
    }
    void Dispatcher(struct epoll_event rev[], int num)
    {
        //
        for (int i = 0; i < num; i++)
        {
            uint32_t event = rev[i].events;
            int fd = rev[i].data.fd;
            if (event & EVENT_IN)
            {
                if (fd == _listen_ptr->Fd())
                {
                    Accepter();
                }
                else
                {
                    Recver(fd);
                }
            }
            else if (event & EVENT_OUT)
            {
            }
            else
            {
            }
        }
    }
    void Start()
    {
        // 将_listen添加到epoll中
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, _listen_ptr->Fd(), EVENT_IN);
        struct epoll_event revs[num];
        for (;;)
        {
            int n = _epoller_ptr->EpollWait(revs, num);
            if (n > 0)
            {
                // 存在就绪事件
                lg(Debug, "event happend fd:", revs[0].data.fd);
                Dispatcher(revs, n);
            }
            else if (n == 0)
            {
                lg(Info, "time out...\n");
            }
            else
            {
                lg(Error, "epoll wait error");
            }
        }
    }
    ~EpollServer()
    {
        _listen_ptr->Close();
    }
};